/*
 * FSWUniCEUB - 2008
 * 
 * Este arquivo é de propriedade da Fábrica de Software do UniCEUB
 * 
 * Para mais informações entre em contato com a coordenação do
 * curso de Ciência da Computação da instituição.
 */

package br.uniceub.fsw.indicadores.util;

import java.util.Timer;
import javax.servlet.http.HttpServlet;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.ServletException;
import java.util.Calendar;
import java.util.TimerTask;
import java.util.List;
import java.util.Date;
import java.text.SimpleDateFormat;
import br.uniceub.fsw.indicadores.bo.ValorIndicadorBO;
import br.uniceub.fsw.indicadores.vo.IndicadorVO;
import br.uniceub.fsw.indicadores.dao.PlanoMedicaoDAO;
import br.uniceub.fsw.indicadores.dao.IndicadorDAO;

/**
 *  O Agente é resposável pela chamada aos procedimentos de cálculo de indicadores.
 *  É iniciado junto com o contexto da aplicação. Adicionar ao web.xml:
 * 
 * <servlet>
 *      <servlet-name>Calculo</servlet-name>
 *      <servlet-class>br.uniceub.fsw.indicadores.util.CalculoServlet</servlet-class>
 *      <load-on-startup>1</load-on-startup> 
 *      <init-param>
 *          <param-name>hora</param-name>
 *          <param-value>10</param-value>
 *      </init-param>
 *      <init-param>
 *          <param-name>minuto</param-name>
 *          <param-value>0</param-value>
 *      </init-param>
 * </servlet>
 * 
 * @author Marcos Mello Nobrega Soares ra 20136178
 */
public class CalculoServlet extends HttpServlet {
    
    /**
     * Hora do dia de execução de cálculo dos indicadores
     */
    private int hora;
    
     /**
     * Minuto da hora do dia de execução de cálculo dos indicadores
     */
    private int minuto;
 
    public int getHora() {
        return hora;
    }
    
    public void setHora(int hora) {
        if(hora > 23)
            hora = 23;
        else if(hora < 0)
            hora = 0;
        this.hora = hora;
    }
    
    public int getMinuto() {
        return minuto;
    }
    
    public void setMinuto(int minuto) {
        if(minuto > 23)
            minuto = 23;
        else if(minuto < 0)
            minuto = 0;
        this.minuto = minuto;
    }    
   
    @Override
    public void destroy() {  
    }  

    @Override
    public void service(ServletRequest arg0, ServletResponse arg1) throws ServletException {  
    }
	
    @Override
    public void init() throws ServletException {
        
        try {
            setHora(Integer.parseInt(getInitParameter("hora")));            
        }
        catch(NumberFormatException e) {
            setHora(0);
        }
        
        try {
            setMinuto(Integer.parseInt(getInitParameter("minuto")));           
        }
        catch(NumberFormatException e) {
            setMinuto(0);
        }  
        
        Calendar hoje = Calendar.getInstance();         
        SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy hh:mm:ss");
        
        // Passou da hora de executar
        if(hoje.get(Calendar.HOUR_OF_DAY) > hora || (hoje.get(Calendar.HOUR_OF_DAY) == hora && hoje.get(Calendar.MINUTE) > minuto)) {
            
            // Executar imediatamente
            System.out.println(sdf.format(hoje.getTime()) + "(CalculoServlet): Chamando agente de cálculo imediatamente.");
            new AgenteCalculo().run();
            // Agendar pro dia seguinte e agendar pro dia seguinte
            agendar(null);
        }
        else 
            agendar(hoje); // Executar hoje na hora e minutos configurados

    }
    
    /**
     * Agenda e execução do cálculo para data indicada "inicio" ou pro dia seguinte caso a data seja nula.
     * 
     * @param inicio
     */
    public void agendar(Calendar inicio) {
        
        AgenteCalculo agente;
        
        if(inicio == null) {
            inicio = Calendar.getInstance();
            
            // Agendar pro dia seguinte
            inicio.add(Calendar.DAY_OF_MONTH,1);
            
            // Início do dia de hoje
            inicio.set(Calendar.HOUR_OF_DAY, hora);
            inicio.set(Calendar.MINUTE, minuto);
            inicio.set(Calendar.SECOND, 0);
            inicio.set(Calendar.MILLISECOND, 0);
        }       
           
        agente = new AgenteCalculo();
        agente.setCalculoServlet(this);
        
        SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy hh:mm:ss");
        System.out.println(sdf.format(new Date()) + " (CalculoServlet): Agendando agente de cálculo para " + sdf.format(inicio.getTime()) + ".");
        
        new Timer().schedule(agente,inicio.getTime());        
    }
    
    /**
     * Executa os cálculos.
     *
     * @author Marcos Mello Nobrega Soares ra 20136178
     */
    class AgenteCalculo extends TimerTask {
        
        protected CalculoServlet servlet;
        
        public void setCalculoServlet(CalculoServlet servlet) {
            this.servlet = servlet;
        }

        /**
         * Obtém a lista de indicadores do plano vigente e executa seus procedimentos de cálculo.
         */
        public void run() {      
            IndicadorVO indicador = new IndicadorVO();
            IndicadorDAO dao = new IndicadorDAO();

            indicador.setStIndicadorAtivo("S");
            indicador.setStIndicadorManual("N");
            List<IndicadorVO> listaIndicadores = dao.consultarPorPlanoMedicao(new PlanoMedicaoDAO().consultarPlanoVigente(),indicador);

            if(listaIndicadores != null) {
                SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy hh:mm:ss");
                
                System.out.println(sdf.format(new Date()) + " (CalculoServlet): Iniciando cálculo de indicadores.");
                
                for(int i = 0; i < listaIndicadores.size(); i++) {
                    try {
                        Calendar hoje = Calendar.getInstance();
                        Calendar teste = Calendar.getInstance();

                        // Início do dia de hoje
                        hoje.set(Calendar.HOUR_OF_DAY, 0);
                        hoje.set(Calendar.MINUTE, 0);
                        hoje.set(Calendar.SECOND, 0);
                        hoje.set(Calendar.MILLISECOND, 0);

                        indicador = listaIndicadores.get(i);

                        if(indicador.getDtUltimoCalculoIndicador() != null) {
                            Calendar dataInicioCalculo = Calendar.getInstance();                                   
                            
                            teste.setTimeInMillis(indicador.getDtUltimoCalculoIndicador().getTime());
                            
                            // Ajustar dia do mês da data de teste para o dia da data de início de cálculo do indicador 
                            // (Caso o ultimo cálculo tenha sido executado atrasado)
                            if(indicador.getDtInicioCalculoIndicador() != null) {
                                dataInicioCalculo.setTime(indicador.getDtInicioCalculoIndicador()); 
                                if(dataInicioCalculo.get(Calendar.DAY_OF_MONTH) != teste.get(Calendar.DAY_OF_MONTH))
                                    teste.set(Calendar.DAY_OF_MONTH,dataInicioCalculo.get(Calendar.DAY_OF_MONTH));
                            }

                            // Calcular a data para execução do cálculo
                            switch(indicador.getPeriodicidade().getIdPeriodicidade()) {
                                case 1: // Diária
                                    teste.add(Calendar.DAY_OF_MONTH,1);
                                    break;
                                case 2: // Semanal
                                    teste.add(Calendar.DAY_OF_MONTH,7);
                                    break;
                                case 3: // Quinzenal
                                    if(teste.get(Calendar.DAY_OF_MONTH) >= 15) {
                                        teste.add(Calendar.MONTH, 1);
                                        teste.set(Calendar.DAY_OF_MONTH,1);
                                    }
                                    else 
                                        teste.set(Calendar.DAY_OF_MONTH,15);
                                    break;
                                case 4: // Mensal
                                    teste.add(Calendar.MONTH, 1);
                                    break;
                                case 5: // Bimestral
                                    teste.add(Calendar.MONTH, 2);
                                    break;
                                case 6: // Trimestral
                                    teste.add(Calendar.MONTH, 3);
                                    break;
                                case 7: // Semestral
                                    teste.add(Calendar.MONTH, 6);
                                    break;
                                case 8: // Anual
                                    teste.add(Calendar.YEAR, 1);
                                    break;
                            }   
                            
                        }
                        else if(indicador.getDtInicioCalculoIndicador() != null)
                            teste.setTimeInMillis(indicador.getDtInicioCalculoIndicador().getTime());
                        
                        // Início do dia de cálculo
                        if(teste != null) {
                            teste.set(Calendar.HOUR_OF_DAY, 0);
                            teste.set(Calendar.MINUTE, 0);
                            teste.set(Calendar.SECOND, 0);
                            teste.set(Calendar.MILLISECOND, 0);

                            // Hoje é dia ou já passou o dia de cálculo deste indicador
                            if(hoje.compareTo(teste) >= 0)
                                new ValorIndicadorBO().calculaIndicador(listaIndicadores.get(i).getIdIndicador(), false);
                        }
                    }
                    catch(Exception e) {
                        // log de erro
                    }                
                }
                
                System.out.println(sdf.format(new Date()) + " (CalculoServlet): Término do cálculo de indicadores.");
            }
            
            // Agendar pro dia seguinte
            if(servlet != null) 
                servlet.agendar(null);                
        }
    }
}